package com.yanfan.iot.service.impl;

import com.yanfan.common.core.domain.AjaxResult;
import com.yanfan.common.core.redis.RedisCache;
import com.yanfan.common.core.redis.RedisKeyBuilder;
import com.yanfan.common.utils.DateUtils;
import com.yanfan.common.utils.StringUtils;
import com.yanfan.iot.cache.ITSLCache;
import com.yanfan.iot.cache.ITSLValueCache;
import com.yanfan.iot.domain.DeviceTemplate;
import com.yanfan.iot.domain.Product;
import com.yanfan.iot.domain.ThingsModelTemplate;
import com.yanfan.iot.domain.vo.CommonVo;
import com.yanfan.iot.mapper.*;
import com.yanfan.iot.model.*;
import com.yanfan.iot.service.IProductService;
import com.yanfan.iot.service.IThingsModelTemplateService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 产品Service业务层处理
 *
 * @author kerwincui
 * @date 2021-12-16
 */
@Service
public class ProductServiceImpl implements IProductService {

    @Autowired
    private ThingsModelServiceImpl thingsModelService;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductAuthorizeMapper productAuthorizeMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ToolServiceImpl toolService;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceTemplateMapper templateMapper;

    @Autowired
    private IThingsModelTemplateService modelTemplateService;
    @Resource
    private SceneDeviceMapper sceneDeviceMapper;
    @Resource
    private ITSLValueCache thingModelCache;
    @Resource
    private ITSLCache itslCache;

    // 产品默认账号
    @Value("${spring.mqtt.prod-def-user:yanfan}")
    private String prodDefUsername;

    /**
     * 查询产品
     *
     * @param productId 产品主键
     * @return 产品
     */
    @Cacheable(value = "product", key = "#root.methodName + '_' + #productId", unless = "#result == null")
    @Override
    public Product selectProductByProductId(Long productId) {
        return productMapper.selectProductByProductId(productId);
    }

    /**
     * 查询产品列表
     *
     * @param product 产品
     * @return 产品
     */
    @Override
    public List<Product> selectProductList(Product product) {
        List<Product> productList = productMapper.selectProductList(product);
        // 组态不是所有人都买了，故单独查询组态信息
        List<String> guidList = productList
                .stream()
                .map(Product::getGuid)
                .filter(StringUtils::isNotEmpty)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(guidList)) {
            List<Product> scadaList = productMapper.selectListScadaIdByGuidS(guidList);
            Map<String, Long> map = scadaList.stream().collect(Collectors.toMap(Product::getGuid, Product::getScadaId));
            for (Product product1 : productList) {
                Long scadaId = map.get(product1.getGuid());
                product1.setScadaId(scadaId);
            }
        }
        return productList;
    }

    /**
     * 查询产品简短列表
     *
     * @return 产品
     */
    @Override
    public List<IdAndName> selectProductShortList(Product product) {
        return productMapper.selectProductShortList(product);
    }

    /**
     * 新增产品
     *
     * @param product 产品
     * @return 结果
     */
    @CachePut(value = "product", key = "'selectProductByProductId_' + #root.args[0].productId")
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Product insertProduct(Product product) {
        // mqtt账号密码
        if (product.getMqttAccount() == null || product.getMqttAccount().equals("")) {
            product.setMqttAccount(prodDefUsername);
        }
        if (product.getMqttPassword() == null || product.getMqttPassword().equals("")) {
            product.setMqttPassword("P" + toolService.getStringRandom(15));
        }
        if (product.getMqttSecret() == null || product.getMqttSecret().equals("")) {
            product.setMqttSecret("K" + toolService.getStringRandom(15));
        }
        product.setStatus(product.getStatus() == null ? 1 : product.getStatus());
        product.setCreateTime(DateUtils.getNowDate());
        productMapper.insertProduct(product);
        /*如果产品属于modbus协议（云端轮询）需要记录变量模板与产品关系*/
        if (product.getTemplateId() != null) {
            DeviceTemplate template = new DeviceTemplate();
            template.setProductId(product.getProductId());
            template.setTemplateId(product.getTemplateId());
            templateMapper.insertDeviceTemplate(template);

            //将模板物模型导入到物模型表
            List<ThingsModelTemplate> thingsModelTemplates = modelTemplateService.selectAllByTemplateId(product.getTemplateId());
            Long[] ids = thingsModelTemplates.stream().map(ThingsModelTemplate::getTemplateId).toArray(Long[]::new);
            ImportThingsModelInput input = new ImportThingsModelInput();
            input.setTemplateIds(ids);
            input.setProductId(product.getProductId());
            input.setProductName(product.getProductName());
            thingsModelService.importByTemplateIds(input);
        }
        return product;
    }

    /**
     * 修改产品
     *
     * @param product 产品
     * @return 结果
     */
    @Caching(put = {@CachePut(value = "product", key = "'selectProductByProductId_' + #root.args[0].productId")})
    @Override
    public int updateProduct(Product product) {
        product.setUpdateTime(DateUtils.getNowDate());
        return productMapper.updateProduct(product);
    }

    /**
     * 获取产品下面的设备数量
     *
     * @param productId 产品ID
     * @return 结果
     */
    @Cacheable(value = "product", key = "#root.methodName + '_' + #productId", unless = "#result == null")
    @Override
    public int selectDeviceCountByProductId(Long productId) {
        return deviceMapper.selectDeviceCountByProductId(productId);
    }

    /**
     * 更新产品状态,1-未发布，2-已发布
     *
     * @param model
     * @return 结果
     */
    @Override
    @Transactional
    public AjaxResult changeProductStatus(ChangeProductStatusModel model) {
        if (model.getStatus() != 1 && model.getStatus() != 2) {
            return AjaxResult.error("状态更新失败,状态值有误");
        }
        if (model.getStatus() == 2) {
            // 不需要一定要有物模型才可以发布
            updateDeviceStatusByProductIdAsync(model.getProductId());
            //更新物模型缓存
            itslCache.setCacheThingsModelByProductId(model.getProductId());
        }
        if (productMapper.changeProductStatus(model) > 0) {
            return AjaxResult.success("操作成功");
        }
        return AjaxResult.error("状态更新失败");
    }

    /***
     * 更新产品下所有设备的物模型值
     * @param productId
     */
    @Async
    public void updateDeviceStatusByProductIdAsync(Long productId) {
        List<String> deviceNumbers = deviceMapper.selectSerialNumberByProductId(productId);
        deviceNumbers.forEach(x -> {
            // 缓存新的物模型值
            thingModelCache.addCacheDeviceStatus(productId, x);
        });
    }

    /**
     * 批量删除产品
     *
     * @param productIds 需要删除的产品主键
     * @return 结果
     */
    @CacheEvict(value = "product", allEntries = true)
    @Override
    @Transactional
    public AjaxResult deleteProductByProductIds(Long[] productIds) {
        // 删除物模型JSON缓存
        for (int i = 0; i < productIds.length; i++) {
            String key = RedisKeyBuilder.buildTSLCacheKey(productIds[i]);
            redisCache.deleteObject(key);
        }
        // 产品下不能有固件
        int firmwareCount = productMapper.firmwareCountInProducts(productIds);
        if (firmwareCount > 0) {
            return AjaxResult.error("删除失败，请先删除对应产品下的固件");
        }
        // 产品下不能有设备
        int deviceCount = productMapper.deviceCountInProducts(productIds);
        if (deviceCount > 0) {
            return AjaxResult.error("删除失败，请先删除对应产品下的设备");
        }
        // 产品下不能有场景联动
        List<SceneDeviceBindVO> sceneDeviceBindVOList = sceneDeviceMapper.listSceneProductBind(productIds);
        if (CollectionUtils.isNotEmpty(sceneDeviceBindVOList)) {
            String sceneNames = sceneDeviceBindVOList
                    .stream()
                    .map(SceneDeviceBindVO::getSceneName)
                    .collect(Collectors.joining("，"));
            return AjaxResult.error("删除失败，请先修改或删除对应产品下的场景联动：" + sceneNames);
        }
        // 删除产品物模型
        productMapper.deleteProductThingsModelByProductIds(productIds);
        // 删除产品的授权码
        productAuthorizeMapper.deleteProductAuthorizeByProductIds(productIds);
        // 删除产品
        if (productMapper.deleteProductByProductIds(productIds) > 0) {
            return AjaxResult.success("删除成功");
        }
        return AjaxResult.error("删除失败");
    }


    /**
     * 删除产品信息
     *
     * @param productId 产品主键
     * @return 结果
     */
    @CacheEvict(value = "product", allEntries = true)
    @Override
    public int deleteProductByProductId(Long productId) {
        // 删除物模型JSON缓存
        redisCache.deleteObject(RedisKeyBuilder.buildTSLCacheKey(productId));
        return productMapper.deleteProductByProductId(productId);
    }

    /**
     * 根据设备编号查询产品信息
     *
     * @param serialNumber 设备编号
     * @return 结果
     */
    @Cacheable(value = "product", key = "#root.methodName + '_' + #serialNumber", unless = "#result == null")
    @Override
    public Product getProductBySerialNumber(String serialNumber) {
        return productMapper.getProductBySerialNumber(serialNumber);
    }

    /**
     * 根据设备编号查询协议编号
     *
     * @param serialNumber 设备编号
     * @return 协议编号
     */
    @Cacheable(value = "product", key = "#root.methodName + '_' + #serialNumber", unless = "#result == null")
    @Override
    public ProductCode getProtocolBySerialNumber(String serialNumber) {
        return productMapper.getProtocolBySerialNumber(serialNumber);
    }

    /**
     * 根据产品id获取协议编号
     *
     * @param productId 产品id
     * @return 协议编号
     */
    @Cacheable(value = "product", key = "#root.methodName + '_' + #productId", unless = "#result == null")
    @Override
    public String getProtocolByProductId(Long productId) {
        return productMapper.getProtocolByProductId(productId);
    }


    /**
     * 根据模板id查询所有使用的产品
     *
     * @param templeId 模板id
     * @return
     */
    @Cacheable(value = "product", key = "#root.methodName + '_' + #templeId", unless = "#result == null")
    @Override
    public List<Product> selectByTempleId(Long templeId) {
        return productMapper.selectByTempleId(templeId);
    }

    @Override
    public String selectImgUrlByProductId(Long productId) {
        return productMapper.selectImgUrlByProductId(productId);
    }

    @Override
    public List<Product> selectTerminalUserProduct(Product product) {
        return productMapper.selectTerminalUserProduct(product);
    }

    /**
     * 根据产品id获取关联组态guid
     *
     * @param productId
     * @return guid
     */
    @Override
    public String selectGuidByProductId(Long productId) {
        return productMapper.selectGuidByProductId(productId);
    }

    /**
     * 查询产品统计
     *
     * @return 结果
     */
    @Override
    public CommonVo selectCount() {
        return productMapper.selectCount();
    }
}
